package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.RefundRecordApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.ExecutionResultResDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersCancelledMapper;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.OrdersRefundMapper;
import com.jzo2o.health.mapper.ReservationSettingMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.Member;
import com.jzo2o.health.model.domain.Orders;
import com.jzo2o.health.model.domain.OrdersCancelled;
import com.jzo2o.health.model.domain.Setmeal;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.model.dto.response.OrdersDetailResDTO;
import com.jzo2o.health.model.dto.response.OrdersPayResDTO;
import com.jzo2o.health.model.dto.response.OrdersResDTO;
import com.jzo2o.health.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.IMemberService;
import com.jzo2o.health.service.IOrdersService;
import com.jzo2o.health.service.ISetmealService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {
    @Autowired
    private ISetmealService setmealService;
    @Autowired
    private IMemberService memberService;
    @Autowired
    private NativePayApi nativePayApi;
    @Autowired
    private TradeProperties tradeProperties;
    @Autowired
    private TradingApi tradingApi;
    @Autowired
    private OrdersCancelledMapper ordersCancelledMapper;
    @Autowired
    private ReservationSettingMapper reservationSettingMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private RefundRecordApi refundRecordApi;
    @Autowired
    private OrdersRefundMapper ordersRefundMapper;

    /**
     * 用户端预约下单
     *
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public PlaceOrderResDTO place(PlaceOrderReqDTO dto) {

        ReservationSetting reservationSetting = reservationSettingMapper.selectOne(Wrappers.<ReservationSetting>lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate()));

        //判断是否可以继续预约
        if (reservationSetting.getNumber() <= 0) {
            throw new ForbiddenOperationException("当日预约人数已达上限");
        }


        //判断订单信息是否存在
        if (ObjectUtils.isEmpty(dto)) {
            throw new ForbiddenOperationException("订单信息不能为空");
        }
        //新建订单
        Orders orders = new Orders();
        //订单添加-体检人信息
        orders.setReservationDate(dto.getReservationDate());
        orders.setCheckupPersonName(dto.getCheckupPersonName());
        orders.setCheckupPersonSex(dto.getCheckupPersonSex());
        orders.setCheckupPersonPhone(dto.getCheckupPersonPhone());
        orders.setCheckupPersonIdcard(dto.getCheckupPersonIdcard());
        //查询订单中的套餐信息
        Setmeal setmeal = setmealService.getById(dto.getSetmealId());
        //订单添加-套餐信息
        orders.setSetmealId(dto.getSetmealId());
        orders.setSetmealName(setmeal.getName());
        orders.setSetmealPrice(new BigDecimal(setmeal.getPrice()));
        orders.setSetmealSex(setmeal.getSex());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealRemark(setmeal.getRemark());
        //订单信息补充
        orders.setOrderStatus(0);
        orders.setPayStatus(0);
        //当前用户ID
        orders.setMemberId(UserThreadLocal.currentUserId());
        Member member = memberService.getById(UserThreadLocal.currentUserId());
        orders.setMemberPhone(member.getPhone());
        //补充时间戳
        orders.setSortBy(new Date().getTime());
        //保存订单信息
        this.save(orders);

        //更新当日预约人数
        Integer reservations = reservationSetting.getReservations();
        reservationSetting.setReservations(reservations + 1);
        reservationSetting.setNumber(reservationSetting.getNumber() - 1);
        reservationSettingMapper.updateById(reservationSetting);

        return new PlaceOrderResDTO(orders.getId());
    }

    /**
     * 用户端生成支付二维码
     *
     * @param id
     * @param tradingChannel
     * @return
     */
    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {
        //1. 根据订单号查询订单是否存在, 不存在返回错误提示
        Orders orders = this.getById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        //2. 判断订单是否为已支付状态,如果是,直接封装返回
        if (orders.getPayStatus() == 1 && StringUtils.isNotBlank(orders.getTransactionId())) {
            throw new ForbiddenOperationException("订单已支付");
        }
        //3. 如果未支付,调用支付微服务生成二维码返回
        NativePayReqDTO nativePayDTO = new NativePayReqDTO();
        nativePayDTO.setTradingChannel(tradingChannel);//支付渠道
        if (ObjectUtil.equal(tradingChannel, PayChannelEnum.ALI_PAY)) {
            //支付宝支付
            nativePayDTO.setEnterpriseId(tradeProperties.getAliEnterpriseId());
        } else {
            //微信支付
            nativePayDTO.setEnterpriseId(tradeProperties.getWechatEnterpriseId());
        }
        if (StringUtils.isNotBlank(orders.getTradingChannel()) && !StringUtils.equals(orders.getTradingChannel(), tradingChannel.toString())) {
            nativePayDTO.setChangeChannel(true);//是否切换频道
        } else {
            nativePayDTO.setChangeChannel(false);//是否切换频道
        }
        nativePayDTO.setProductAppId("jzo2o.c.orders");//业务系统标识
        nativePayDTO.setProductOrderNo(id);//业务系统订单号
        nativePayDTO.setTradingAmount(orders.getSetmealPrice());//交易金额
        nativePayDTO.setMemo(orders.getSetmealName());//备注
        NativePayResDTO nativePayResDTO = nativePayApi.createDownLineTrading(nativePayDTO);
        OrdersPayResDTO ordersPayResDTO = BeanUtil.copyProperties(nativePayResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(0);//待支付
        //5. 更新订单信息
        this.lambdaUpdate()
                .eq(Orders::getId, nativePayResDTO.getProductOrderNo())
                .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())//交易系统订单号【对于三方来说：商户订单】
                .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel().toString())//支付渠道【支付宝、微信、现金、免单挂账】
                .update();
        return ordersPayResDTO;
    }

    /**
     * 用户端确认支付结果
     *
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO payResult(Long id) {
        //判断订单是否存在
        Orders orders = this.getById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        //待支付 且 支付服务交易单号存在
        if (orders.getPayStatus() == 0 && ObjectUtil.isNotEmpty(orders.getTradingOrderNo())) {
            //调用微信查询
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
            //如果支付成功这里更新订单状态
            if (ObjectUtil.isNotNull(tradingResDTO) && ObjectUtil.equals(tradingResDTO.getTradingState(), TradingStateEnum.YJS)) {
                //根据支付状态修改订单状态
                Integer statusCode = tradingResDTO.getTradingState().getCode();
                boolean update = lambdaUpdate()
                        .set(ObjectUtil.equal(statusCode, TradingStateEnum.YJS.getCode()), Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus())
                        .set(ObjectUtil.equal(statusCode, TradingStateEnum.FKSB.getCode()), Orders::getOrderStatus, OrderStatusEnum.CLOSED.getStatus())
                        .set(ObjectUtil.equal(statusCode, TradingStateEnum.QXDD.getCode()), Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus())
                        .set(ObjectUtil.equal(statusCode, TradingStateEnum.YJS.getCode()), Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                        .set(ObjectUtil.isNotEmpty(tradingResDTO.getTransactionId()), Orders::getTransactionId, tradingResDTO.getTransactionId())
                        .set(ObjectUtil.isNotEmpty(tradingResDTO.getTransactionId()), Orders::getPayTime, new Date())
                        .eq(Orders::getId, tradingResDTO.getProductOrderNo())
                        .update();
                if (!update) {
                    log.info("更新订单:{}支付成功失败", orders.getId());
                    throw new CommonException("更新订单" + orders.getId() + "支付成功失败");
                }
            }
        }
        //返回结果
        Orders newOrders = this.getById(id);//查询最新数据
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setPayStatus(newOrders.getPayStatus());
        ordersPayResDTO.setProductOrderNo(newOrders.getId());
        ordersPayResDTO.setTradingOrderNo(newOrders.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(newOrders.getTradingChannel());
        return ordersPayResDTO;
    }

    /**
     * 管理端分页查询订单
     * @return
     */
    @Override
    public PageResult<OrdersResDTO> adminPageQuery(OrdersPageQueryReqDTO ordersPageQueryReqDTO) {
        Page<Orders> page = this.lambdaQuery()
                .eq(ordersPageQueryReqDTO.getOrderStatus() != null, Orders::getOrderStatus, ordersPageQueryReqDTO.getOrderStatus())
                .eq(StrUtil.isNotEmpty(ordersPageQueryReqDTO.getMemberPhone()), Orders::getMemberPhone, ordersPageQueryReqDTO.getMemberPhone())
                .page(new Page<>(ordersPageQueryReqDTO.getPageNo(), ordersPageQueryReqDTO.getPageSize()));

        return new PageResult<>(page.getPages(),page.getTotal(),BeanUtil.copyToList(page.getRecords(),OrdersResDTO.class));

    }

    /**
     * 查询各个状态的订单数量
     * @return
     */
    @Override
    public OrdersCountResDTO countByStatus() {
        List<Orders> list = this.list();
        if (CollUtil.isEmpty(list)) {
            throw new ForbiddenOperationException("订单为空");
        }
        Integer noPayCount = Math.toIntExact(list.stream().filter(e -> e.getOrderStatus() == 0).count());
        Integer waitingCheckupCount = Math.toIntExact(list.stream().filter(e -> e.getOrderStatus() == 100).count());
        Integer completedCheckupCount = Math.toIntExact(list.stream().filter(e -> e.getOrderStatus() == 200).count());
        Integer closedCount = Math.toIntExact(list.stream().filter(e -> e.getOrderStatus() == 300).count());
        Integer cancelledCount = Math.toIntExact(list.stream().filter(e -> e.getOrderStatus() == 400).count());
        int totalCount = this.count();
        return new OrdersCountResDTO(noPayCount,waitingCheckupCount,completedCheckupCount,closedCount,cancelledCount,totalCount);

    }

    /**
     * 管理端查询订单详情
     * @param id
     * @return
     */
    @Override
    public AdminOrdersDetailResDTO aggregation(Long id) {
        Orders orders = this.getById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        //得到orderInfo对象
        AdminOrdersDetailResDTO.OrderInfo orderInfo = BeanUtil.copyProperties(orders, AdminOrdersDetailResDTO.OrderInfo.class);
        //主键查询订单取消信息
        OrdersCancelled ordersCancelled = ordersCancelledMapper.selectById(id);
        AdminOrdersDetailResDTO.CancelInfo cancelInfo = BeanUtil.copyProperties(ordersCancelled, AdminOrdersDetailResDTO.CancelInfo.class);
        //设置refundInfo对象
        AdminOrdersDetailResDTO.RefundInfo refundInfo = new AdminOrdersDetailResDTO.RefundInfo();
        refundInfo.setRefundStatus(orders.getPayStatus());
        refundInfo.setTradingChannel(orders.getTradingChannel());
        refundInfo.setCancelTime(cancelInfo.getCancelTime());
        refundInfo.setCancelReason(cancelInfo.getCancelReason());
        refundInfo.setRefundId(orders.getRefundId());
        //设置payInfo对象
        AdminOrdersDetailResDTO.PayInfo payInfo = new AdminOrdersDetailResDTO.PayInfo();
        payInfo.setTradingChannel(orders.getTradingChannel());
        payInfo.setPayTime(orders.getPayTime());
        payInfo.setThirdOrderId(orders.getTransactionId());
        payInfo.setPayStatus(orders.getPayStatus());
        //封装结果对象
        AdminOrdersDetailResDTO adminOrdersDetailResDTO = new AdminOrdersDetailResDTO();
        adminOrdersDetailResDTO.setOrderInfo(orderInfo);
        adminOrdersDetailResDTO.setPayInfo(payInfo);
        adminOrdersDetailResDTO.setRefundInfo(refundInfo);
        adminOrdersDetailResDTO.setCancelInfo(cancelInfo);
        return adminOrdersDetailResDTO;
    }
    //用户端取消未支付订单
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(OrdersCancelReqDTO ordersCancelReqDTO) {
        // 用户下单后为待支付状态，可以取消订单，在此情况下订单状态变化：待支付-->已取消
        Orders orders = ordersMapper.selectById(ordersCancelReqDTO.getId());
        if (ObjectUtil.isNull(orders)) {
            //如果订单不存在，返回错误
            throw new ForbiddenOperationException("订单不存在");
        }
        // 判断订单当前状态是否为待支付
        if (!orders.getOrderStatus().equals(OrderStatusEnum.NO_PAY.getStatus())) {
            throw new ForbiddenOperationException("订单状态错误");
        }
        // 订单存在，状态正确，修改订单的状态
        orders.setOrderStatus(OrderStatusEnum.CANCELLED.getStatus());
        ordersMapper.updateById(orders);
        // 同时该操作需要取消预约，例如：用户预约日期为2023年10月1日，已预约人数为100人，该操作会导致已预约人数变更为99人。


        ReservationSetting reservationSetting = reservationSettingMapper.selectOne(Wrappers.<ReservationSetting>lambdaQuery()
                .eq(ReservationSetting::getOrderDate, orders.getReservationDate()));

        //更新当日预约人数
        reservationSetting.setReservations(reservationSetting.getReservations() - 1);
        reservationSetting.setNumber(reservationSetting.getNumber() + 1);
        reservationSettingMapper.updateById(reservationSetting);

    }

    //订单分页查询
    @Override
    public List<OrdersResDTO> findByPage(Integer ordersStatus, Long sortBy) {

        List<Orders> list = lambdaQuery().eq(ObjectUtil.isNotNull(ordersStatus), Orders::getOrderStatus, ordersStatus)
                .lt(ObjectUtil.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .orderByDesc(Orders::getSortBy)
                .list();

        List<OrdersResDTO> ordersResDTOS = list.stream().map(e -> BeanUtil.copyProperties(e, OrdersResDTO.class))
                .collect(Collectors.toList());
        return ordersResDTOS;
    }

    //根据订单id查询
    @Override
    public OrdersDetailResDTO detail(Long id) {
        return BeanUtil.toBean(ordersMapper.selectById(id), OrdersDetailResDTO.class);
    }

    //订单退款
    @Override
    @Transactional
    public void refund(OrdersCancelReqDTO ordersCancelReqDTO) {
        Orders orders = ordersMapper.selectById(ordersCancelReqDTO.getId());
        if (ObjectUtil.isNull(orders)) {
            //如果订单不存在，返回错误
            throw new ForbiddenOperationException("订单不存在");
        }
        // 判断订单当前支付状态是否为已支付,订单状态是否为待体检
        if (!orders.getPayStatus().equals(OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                || !orders.getOrderStatus().equals(OrderStatusEnum.WAITING_CHECKUP.getStatus())) {
            throw new ForbiddenOperationException("订单支付状态错误");
        }
        // 订单存在，状态正确，修改订单的状态以及订单支付状态
        orders.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.REFUNDING.getStatus());
        ordersMapper.updateById(orders);
        // 同时该操作需要取消预约
        ReservationSetting reservationSetting = reservationSettingMapper.selectOne(Wrappers.<ReservationSetting>lambdaQuery()
                .eq(ReservationSetting::getOrderDate, orders.getReservationDate()));
        //更新当日预约人数
        reservationSetting.setReservations(reservationSetting.getReservations() - 1);
        reservationSetting.setNumber(reservationSetting.getNumber() + 1);
        reservationSettingMapper.updateById(reservationSetting);

        //添加待退款记录
        OrdersRefund ordersRefund = new OrdersRefund();
        ordersRefund.setId(ordersCancelReqDTO.getId());
        ordersRefund.setTradingOrderNo(orders.getTradingOrderNo());
        ordersRefund.setRealPayAmount(orders.getSetmealPrice());
        ordersRefundMapper.insert(ordersRefund);

        //新启动一个线程请求第三方退款接口
        new Thread(() -> {
            //休眠5s
            ThreadUtil.sleep(5, TimeUnit.SECONDS);

            //查询退款记录
            OrdersRefund ordersRefundFromDB = ordersRefundMapper.selectById(ordersCancelReqDTO.getId());
            if (ObjectUtil.isNotNull(ordersRefundFromDB)) {
                //请求退款
                requestRefundOrder(ordersRefundFromDB);
            }
        }).start();


    }
    private void requestRefundOrder(OrdersRefund ordersRefundFromDB) {
        // 远程调用trade微服务，调用退款接口
        ExecutionResultResDTO executionResultResDTO = refundRecordApi.refundTrading(ordersRefundFromDB.getTradingOrderNo(), ordersRefundFromDB.getRealPayAmount());
        //处理退款后内容
        if (executionResultResDTO != null) {
            //1. 从响应结果中确定退款状态
            if (executionResultResDTO.getRefundStatus() <= 1) {
                return;//0:发起退款 1：退款中
            }

            //2. 更新订单表中的支付状态
            Orders orders = BeanUtil.copyProperties(executionResultResDTO, Orders.class);
            orders.setId(ordersRefundFromDB.getId());
            int result = baseMapper.updateById(orders);

            //3. 删除待退款表中记录
            if (result > 0) {
                ordersRefundMapper.deleteById(ordersRefundFromDB.getId());
            }
        }
    }
}

